﻿using System;
using System.IO;
using System.Collections.Generic;

namespace TestingToolkit
{
    /// <summary>
    /// Represents a CommandLineMenu whose execution is automated
    /// </summary>
    public class AutoCommandLineMenu: CommandLineMenu
    {
        private TextReader reader;
        private TextWriter writer;
        private bool exitCondition;
        private bool lockIOFields;

        /// <summary>
        /// Construct a new AutoCommandLineMenu
        /// </summary>
        public AutoCommandLineMenu()
            : base()
        {
            lockIOFields = false;
            reader = null;
            writer = null;
            AddOption("Exit", ExitCallback);
        }

        /// <summary>
        /// DI-Compatible Constructor
        /// </summary>
        public AutoCommandLineMenu(IDictionary<uint, Option> dict)
            : base(dict)
        {
            lockIOFields = false;
            reader = null;
            writer = null;
            AddOption("Exit", ExitCallback);
        }

        private void ExitCallback(string obj)
        {
            exitCondition = true;
        }

        /// <summary>
        /// Continuously display and operate the menu until the user chooses the Exit option
        /// </summary>
        /// <exception cref="System.InvalidDataException">The Reader and/or Writer field is null</exception>
        public void Run()
        {
            if (reader == null || writer == null)
                throw new InvalidDataException("Both the Reader and Writer fields of AutoCommandLineMenu must have a value before calling Run()");

            lockIOFields = true;
            exitCondition = false;

            while (!Exit)
                RunOnce();

            lockIOFields = false;
        }

        /// <summary>
        /// Display and operate the menu one time (regardless of the value of the Exit field)
        /// </summary>
        /// <exception cref="System.InvalidDataException">The Reader and/or Writer field is null</exception>
        public void RunOnce()
        {
            if (reader == null || writer == null)
                throw new InvalidDataException("Both the Reader and Writer fields of AutoCommandLineMenu must have a value before calling RunOnce()");

            lockIOFields = true;

            DisplayMenu(Writer);
            AcquireAndProcessInput(Reader);

            lockIOFields = false;
        }

        /// <summary>
        /// Gets a value indicating whether this menu is waiting to exit
        /// </summary>
        public bool Exit
        {
            get { return exitCondition; }
        }

        /// <summary>
        /// Gets a value indicating whether the values of the instance's IO fields are locked
        /// </summary>
        public bool IOFieldsLocked
        {
            get { return lockIOFields; }
        }

        /// <summary>
        /// Gets or sets the input reader for this menu
        /// </summary>
        /// <exception cref="System.InvalidOperationException">The IO fields of this instance are locked</exception>
        public TextReader Reader
        {
            get { return reader; }
            set
            {
                if (lockIOFields)
                    throw new InvalidOperationException("Cannot change the Reader or Writer fields of an AutoCommandLineMenu instance while IOFieldsLocked is true");
                else
                    reader = value;
            }
        }

        /// <summary>
        /// Gets or sets the output writer for this menu
        /// </summary>
        /// /// <exception cref="System.InvalidOperationException">The IO fields of this instance are locked</exception>
        public TextWriter Writer
        {
            get { return writer; }
            set
            {
                if (lockIOFields)
                    throw new InvalidOperationException("Cannot change the Reader or Writer fields of an AutoCommandLineMenu instance while IOFieldsLocked is true");
                else
                    writer = value;
            }
        }
    }
}
